package v3

import (
	"github.com/boltdb/bolt"
	"log"
)

type BlockChain struct {
	//数据库
	DB *bolt.DB
	//链最后一个hash
	TailHash []byte
}

const (
	blockChainDB        = "blockChain.db"
	blockChainDB_Bucket = "blockChainBucket"
	blockLastHashKey    = "blockLastHashKey"
)

func GetConst_blockChainDB() string {
	return blockChainDB
}
func GetConst_blockChainDB_Bucket() string {
	return blockChainDB_Bucket
}
func GetConst_blockLastHashKey() string {
	return blockLastHashKey
}

func (b *BlockChain) dbExits() bool {
	db, err := bolt.Open(blockChainDB, 0600, nil)
	defer db.Close() //这不能关,其他地方还要用
	if err != nil {
		log.Panic(err)
		return false
	}
	return true
}

// 创建创世区块,第一个初始块
func NewBlockChain(address string) *BlockChain {
	var tailHash []byte
	db, err := bolt.Open(blockChainDB, 0600, nil)
	defer db.Close() //这不能关,其他地方还要用
	if err != nil {
		log.Panic(err)
	}
	//写入数据
	db.Update(func(tx *bolt.Tx) error {
		//获取桶
		bucket := tx.Bucket([]byte(blockChainDB_Bucket))
		if bucket == nil {
			bucket, err := tx.CreateBucket([]byte(blockChainDB_Bucket))
			if err != nil {
				log.Panic("创建桶失败: ", err)
			}
			//创世一次就可以了
			newBlock := NewBlock([]byte{}, "创世 First zero Block")
			//存入当前区块的hash: 区块字节流
			bucket.Put(newBlock.Hash, newBlock.Serialize())
			bucket.Put([]byte(blockLastHashKey), newBlock.Hash)
			//设置最后的hash到内存
			tailHash = newBlock.Hash
		} else {
			//存在桶直接拿取最后的hash值
			tailHash = bucket.Get([]byte(blockLastHashKey))
		}
		return nil
	})
	return &BlockChain{DB: db, TailHash: tailHash}
}

func GetBlockChain() *BlockChain {
	var tailHash []byte
	db, err := bolt.Open(blockChainDB, 0600, nil)
	if err != nil {
		log.Panic(err)
	}
	db.View(func(tx *bolt.Tx) error {
		bucket := tx.Bucket([]byte(blockChainDB_Bucket))
		if bucket == nil {
			log.Panic("不存在的桶")
			return nil
		}
		tailHash = bucket.Get([]byte(blockLastHashKey))
		return nil
	})
	return &BlockChain{DB: db, TailHash: tailHash}
}

func (b *BlockChain) AddBlock(data string) {
	preHash := b.TailHash
	db := b.DB
	newBlock := NewBlock(preHash, data)
	db.Update(func(tx *bolt.Tx) error {
		//获取桶
		bucket := tx.Bucket([]byte(blockChainDB_Bucket))
		if bucket == nil {
			log.Panic("桶不存在,不能添加区块!")
			return nil
		}
		//存入当前区块的hash: 区块字节流
		bucket.Put(newBlock.Hash, newBlock.Serialize())
		bucket.Put([]byte(blockLastHashKey), newBlock.Hash)
		//设置最后的hash到内存
		b.TailHash = newBlock.Hash
		return nil
	})
}
